#!/usr/bin/perl

use strict;

package Lunmgr;
require Exporter;

our @ISA = qw(Exporter);

our @EXPORT_OK = qw(
        resetTimer
        checkTimer
        readDevtable
        writeDevtable
        getMpathForUuid
        getDevForUuid
        getUuidForDev
        getStatusForDev
        getMsaForWwn
    );

our %EXPORT_TAGS = ( 
    'all' => [@EXPORT_OK],
);

use Time::HiRes qw/time/;

use Lunmgr::TableEntry;
use Lunmgr::MSA;

use Data::Dumper;

my %_cmds = (
    multipath => '/sbin/multipath',
    scsi_id => '/sbin/scsi_id',
    egrep => '/bin/egrep',
);

{
  my $_tp;

  sub resetTimer{ $_tp = time; }
  sub checkTimer($)
  {
      my $tag = $_[0];
      my $now = time;
      my $elapse = $now - $_tp;

      $_tp = $now;

      print "Elapsed($tag): $elapse\n";

      $elapse;
  }
}

{ 
  my %_uuid_mpath = ();  # map from uuid to mpath
  sub getMpathForUuid($)
  {
      my $uuid = $_[0];

      if (scalar %_uuid_mpath == 0) {
          chomp (my @lines = `$_cmds{multipath} -ll`);

          foreach (grep {/^mpath/} @lines) {
              my @fields = split /\s+/;
              $fields[1] =~ s/[()]//g;

              $_uuid_mpath{$fields[1]} = $fields[0];
          }
      } 
      $_uuid_mpath{$uuid};
  }

  my %_dev_uuid = ();    # map from dev to uuid
  sub getUuidForDev($)
  {
      my $sddev = $_[0];
      unless ($_dev_uuid{$sddev}) {
          chomp (my $uuid = `$_cmds{scsi_id} -gus /block/$sddev`);
          $_dev_uuid{$sddev} = $uuid;
      }

      $_dev_uuid{$sddev};
  }

  my %_uuid_dev = ();    # map from uuid to dev
  sub getDevForUuid($)
  {
      my $uuid = $_[0];

      if (scalar %_uuid_dev == 0) {
          foreach my $sddev (glob('/dev/sd*')) {
              next if ($sddev =~ /\d$/);
              $sddev =~ s/.*\///;
              my $uuid = getUuidForDev($sddev);

              next unless ($uuid);

              if ($_uuid_dev{$uuid}) {
                  $_uuid_dev{$uuid} .= ",$sddev";
              }
              else {
                  $_uuid_dev{$uuid} = $sddev;
              }
          }
      }

      $_uuid_dev{$uuid};
  }

  my %_dev_pathstat = (); # map from dev to path status
  sub getStatusForDev($)
  {
      my $dev = $_[0];

      if (%_dev_pathstat == 0) {
          my ($sd, $status) = ();
          foreach (`$_cmds{multipath} -ll`) {
              chomp;
              $sd = $1 if (/(sd[a-z]+)/);
              $status = $1 if (/(\[\w+\]\[\w+\])/);

              if ($sd && $status) {
                $_dev_pathstat{$sd} = $status;
                ($sd, $status) = ();
              }
          }
      }

      $_dev_pathstat{$dev};
  }

  my %_wwn_msa = () ; # map from wwn to msa
  ### load all msas, very time consuming, but cannot use multi thread
  sub getMsaForWwn
  {
      my $wwn = $_[0];
      if (%_wwn_msa == 0) {
          chomp (my @allmsas = `$_cmds{egrep} -o msa[0-9]+ /etc/hosts`);
          @allmsas = keys %{{map { $_ => 1 } @allmsas}}; # remove duplicate

          foreach (@allmsas) {
              my $msa = Lunmgr::MSA->new($_);
              $_wwn_msa{$_} = $msa foreach ($msa->wwns);
          }
      }

      $_wwn_msa{$wwn};
  }
}

sub readDevtable($)
{
    my $file = $_[0];

    my @entries;
    open my $fh, "$file" or die "Could not open $file\n";
    while (<$fh>) {
        chomp;
        next if (/^\s*$/);
        next if (/^#/);
        push @entries,  Lunmgr::TableEntry->new($_);
    }

    close $fh;

    \@entries;
}

sub writeDevtable($$)
{
    my ($entry_ref, $file) = @_;

    open my $fh, ">$file" or die "Could not open $file\n";
    
    print $fh "$_\n" foreach (@$entry_ref);

    close $fh;

}

1;
